/*
 * MIT License
 *
 * Copyright (c) 2024-2025 milkpotatoes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

export { };

Reflect.deleteProperty(globalThis, "requireNapiPreview");
Reflect.deleteProperty(globalThis, "requireInternal");

const hiddenMethods = new Set<string>();
const registerTempInnerMethod = (name: string, val: Object) => {
    hiddenMethods.add(name);
    Reflect.set(globalThis, name, val);
}
const unregisterAll = () => {
    hiddenMethods.forEach(e => {
        Reflect.deleteProperty(globalThis, e);
    });
}
registerTempInnerMethod("registerTempInnerMethod", registerTempInnerMethod);
registerTempInnerMethod("unregisterAll", unregisterAll);

const exchangeInnerMethod = <T>(obj: Object, name: string): T => {
    const innerMethod = Reflect.get(obj, name);
    Reflect.deleteProperty(obj, name);
    return innerMethod as T;
}
registerTempInnerMethod("exchangeInnerMethod", exchangeInnerMethod);

enum PropertyConfiguration {
    NONE = 0,
    CONFIGURABLE = 1 << 0,
    ENUMERABLE = 1 << 1,
    WRITABLE = 1 << 2,
    DEFAULT_PROPERTY = CONFIGURABLE | ENUMERABLE | WRITABLE,
    WEC = DEFAULT_PROPERTY,
    W_C = WRITABLE | CONFIGURABLE,
}
interface PropertyDescriptorTemplate {
    configurable: boolean;
    enumerable: boolean;
    writable: boolean;
}
const PROPERTY_CONFIGURATION_TEMPLATE = new Map<PropertyConfiguration, PropertyDescriptorTemplate>();
const getConfigurationTemplate = (configuration: PropertyConfiguration): PropertyDescriptorTemplate => {
    let result = PROPERTY_CONFIGURATION_TEMPLATE.get(configuration);
    if (result !== undefined) return result;
    result = {
        configurable: false,
        enumerable: false,
        writable: false,
    };
    let cacheKey = 0;
    if ((configuration & PropertyConfiguration.CONFIGURABLE) > 0) {
        result.configurable = true;
        cacheKey |= PropertyConfiguration.CONFIGURABLE;
    }
    if ((configuration & PropertyConfiguration.ENUMERABLE) > 0) {
        result.enumerable = true;
        cacheKey |= PropertyConfiguration.ENUMERABLE;
    }
    if ((configuration & PropertyConfiguration.WRITABLE) > 0) {
        result.writable = true;
        cacheKey |= PropertyConfiguration.WRITABLE;
    }
    PROPERTY_CONFIGURATION_TEMPLATE.set(cacheKey, result);
    return result;
}
const exportToObject = (receiver: Object, name: string, value: any,
    configuration: PropertyConfiguration = PropertyConfiguration.DEFAULT_PROPERTY): boolean => {
    return Reflect.defineProperty(receiver, name, {
        value: value,
        ...getConfigurationTemplate(configuration),
    });
}
const exportsToObject = (receiver: Object, values: Iterable<[string, any, PropertyConfiguration?]>): boolean => {
    let result = true;
    for (let pair of values) {
        result &&= exportToObject(receiver, ...pair);
    }
    return result
}
registerTempInnerMethod("PropertyConfiguration", PropertyConfiguration);
registerTempInnerMethod("exportToObject", exportToObject);
registerTempInnerMethod("exportsToObject", exportsToObject);

function queueMicrotask(task: () => void) {
    if (arguments.length < 1) {
        throw new TypeError("Failed to execute 'queueMicrotask' on 'global': " +
            "1 argument required, but only 0 present.");
    }
    if (typeof task !== 'function') {
        throw new TypeError("Failed to execute 'queueMicrotask' on 'global': " +
            "parameter 1 is not of type 'Function'.");
    }
    new Promise<void>((r) => r()).then(task);
}
exportToObject(globalThis, "queueMicrotask", queueMicrotask);
